#include <cstdio>
#include <cstdlib>

#include <iostream>
#ifdef WIN32
#include <winsock2.h>

#endif
#include <evhttp.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/buffer.h>
#include <event2/http.h>
#include <fstream>
#include <istream>

#include <signal.h>

#ifdef WIN32
typedef unsigned long long uint64_t; 
#define nullptr NULL
#endif

#define LOG(x) std::cout << (x) << std::endl;

// #define WRITE_LOG(FORMAT, ...){ \  
//     do{                             \  
//         struct tm* pt;                      \  
//         time_t time_now;                    \  
//         struct timeval tv;                  \  
//         gettimeofday(&tv, NULL);                \  
//         time(&time_now);                    \  
//         pt  = localtime(&(tv.tv_sec));          \  
//         printf("%04d-%02d-%02d %02d:%02d:%02d.%03u "FORMAT"\n", \  
//             pt->tm_year+1900,                \  
//             pt->tm_mon+1,                    \  
//             pt->tm_mday,                 \  
//             pt->tm_hour,                 \  
//             pt->tm_min,                  \  
//             pt->tm_sec,                  \  
//             (u32)(tv.tv_usec)/1000,             \  
//             ##__VA_ARGS__);                 \  
//     }while(0);                          \  
// }  

struct chunk_param
{
    struct evhttp_request* req;
    std::ifstream* in;
};

uint64_t get_stream_size(std::ifstream* f)
{
    std::streampos p = f->tellg();
    f->seekg(0, std::ios::end);
    uint64_t len = f->tellg();
    f->seekg(p);
    return len;
}


void http_gencb(struct evhttp_request* req, void* arg)
{
    const char* uri = evhttp_request_get_uri(req);
    struct evbuffer* buf =  evbuffer_new();
    std::cout << uri << std::endl;
    evbuffer_add_printf(buf, "<html><body><h1>This is libevent http server test!!!</h1></body></html>");
    evhttp_send_reply(req, HTTP_OK, "OK", buf);
    evbuffer_free(buf);
}

void http_chunk_close_cb(struct evhttp_connection* conn, void* arg)
{
    LOG("http_chunk_close_cb end");
    struct chunk_param* param = (struct chunk_param*) arg;
    evhttp_send_reply_end(param->req);
    param->in->close();
    delete param->in;
    delete param; 
}


void http_chunk_cb(struct evhttp_connection* conn, void* arg)
{
    struct chunk_param* param = (struct chunk_param*) arg;
    size_t len;
    char buffer[4096];
    struct evbuffer* buf = evbuffer_new();
    param->in->read(buffer, 4096);
    len = param->in->gcount();
    if (len == 0)
    {
        if (conn)
            evhttp_connection_set_closecb(conn, nullptr, nullptr);
        evhttp_send_reply_end(param->req);
        param->in->close();
        delete param->in;
        delete param;
        LOG("chunk end");        
    }
    else 
    {
        evbuffer_add(buf, buffer, len);
        evhttp_send_reply_chunk_with_cb(param->req, buf, http_chunk_cb, param);
        LOG("chunk dataing...");
    }
    
    evbuffer_free(buf);    
}


void http_down_cb(struct evhttp_request* req, void* arg)
{
    char buffer[1024] = {};
    LOG("/down callback");
    struct chunk_param* param = new chunk_param;
    param->req = req;
#ifdef WIN32
	param->in = new std::ifstream("111.wav", std::ios::binary);
#else
    param->in = new std::ifstream("/home/redfox/mydata/qt-opensource-linux-x64-5.8.0.run", std::ios::binary);
#endif
    if (param->in->fail())
    {
        LOG("Open file faile");
        evhttp_send_error(req, HTTP_NOTFOUND, "Not Found File");
        delete param->in;
        delete param;
    }
    else 
    {
        LOG("chunk start...");
        uint64_t len = get_stream_size(param->in);
#ifdef WIN32
        evutil_snprintf(buffer, sizeof(buffer), "%I64u", len);
#else
		evutil_snprintf(buffer, sizeof(buffer), "%zd", len);
#endif
        struct evhttp_connection* conn = evhttp_request_get_connection(param->req);
        evhttp_connection_set_closecb(conn, http_chunk_close_cb, param);
        //struct bufferevent*  bev = evhttp_connection_get_bufferevent(conn);
        //bufferevent_enable(bev, EV_READ);
        evhttp_connection_set_timeout(conn, 30);
        evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "application/octet-stream");
        evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Length", buffer);
        evhttp_send_reply_start(req, HTTP_OK, "OK");
        http_chunk_cb(nullptr, param);
    } 
}

void exit_sign_callback(evutil_socket_t, short, void * arg)
{
	struct event_base* base = (struct event_base *) arg;
	event_base_loopexit(base, nullptr);
}

int main(int argc, char* argv[])
{
#ifdef WIN32
	WSAData wsData = {};
	WSAStartup(0x0202, &wsData);
#endif
    std::cout << "this is test." << std::endl;


    struct event_base* base = event_base_new();

	struct event* sign_event = evsignal_new(base, SIGINT, exit_sign_callback, base);
	event_add(sign_event, nullptr);

    struct evhttp* http = evhttp_new(base);

    evhttp_set_gencb(http, http_gencb, http);
    evhttp_set_cb(http, "/down", http_down_cb, http);

    std::cout << "http server on 8088.....";
    int code = evhttp_bind_socket(http, "0.0.0.0", 8088);
    if (code == 0)
        std::cout << "Sucessed! (CTRL+C to exit)" << std::endl;
    else
        std::cout << "Failure" << std::endl;
    event_base_dispatch(base);
	LOG("Exiting...");

    evhttp_free(http);
	
	event_free(sign_event);
    event_base_free(base);

	LOG("END");
#ifdef WIN32
	WSACleanup();
#endif
    return 1;
}


